package org.xcolab.view.pages.profile.view; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.validation.SmartValidator; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.xcolab.client.admin.enums.ConfigurationAttributeKey; import org.xcolab.client.emails.EmailClient; import org.xcolab.client.files.FilesClient; import org.xcolab.client.files.exceptions.FileEntryNotFoundException; import org.xcolab.client.files.pojo.FileEntry; import org.xcolab.client.members.MembersClient; import org.xcolab.client.members.MessagingClient; import org.xcolab.client.members.exceptions.MemberNotFoundException; import org.xcolab.client.members.pojo.Member; import org.xcolab.client.members.pojo.MessagingUserPreferences; import org.xcolab.entity.utils.TemplateReplacementUtil; import org.xcolab.view.util.entity.flash.AlertMessage; import org.xcolab.view.util.entity.flash.ErrorMessage; import org.xcolab.util.CountryUtil; import org.xcolab.util.html.HtmlUtil; import org.xcolab.view.errors.ErrorText; import org.xcolab.view.pages.profile.beans.MessageBean; import org.xcolab.view.pages.profile.beans.NewsletterBean; import org.xcolab.view.pages.profile.beans.UserBean; import org.xcolab.view.pages.profile.utils.UserProfilePermissions; import org.xcolab.view.pages.profile.wrappers.UserProfileWrapper; import java.io.IOException; import javax.mail.internet.InternetAddress; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller @RequestMapping("/members/profile") public class UserProfileController { private final static Logger _log = LoggerFactory.getLogger(UserProfileController.class); public static final String SHOW_PROFILE_VIEW = "profile/showUserProfile"; public static final String EDIT_PROFILE_VIEW = "profile/editUserProfile"; @Autowired private SmartValidator validator; public UserProfileController() { } @InitBinder("userBean") public void initUserWrapperBeanBinder(WebDataBinder binder) { binder.setValidator(validator); } @GetMapping public String showProfile(HttpServletRequest request, HttpServletResponse response, Model model, Member member) throws IOException { if (member != null) { response.sendRedirect("/members/profile/" + member.getId_()); return ErrorMessage.ERROR_VIEW; } else { return ErrorText.ACCESS_DENIED.flashAndReturnView(request); } } @GetMapping("{memberId}") public String showUserProfileView(HttpServletRequest request, Model model, @PathVariable long memberId, Member member) { try { UserProfilePermissions permissions = new UserProfilePermissions(member); model.addAttribute("permissions", permissions); model.addAttribute("_activePageLink", "community"); populateUserWrapper(new UserProfileWrapper(memberId, request), model); model.addAttribute("pointsActive", ConfigurationAttributeKey.IS_POINTS_ACTIVE.get()); return SHOW_PROFILE_VIEW; } catch (MemberNotFoundException e) { return ErrorMessage.error("User profile not found").flashAndReturnView(request); } } private void populateUserWrapper(UserProfileWrapper currentUserProfile, Model model) { model.addAttribute("currentUserProfile", currentUserProfile); model.addAttribute("userBean", currentUserProfile.getUserBean()); model.addAttribute("messageBean", new MessageBean()); } @GetMapping("{memberId}/edit") public String showUserProfileEdit(HttpServletRequest request, HttpServletResponse response, Model model, @PathVariable long memberId, Member member) { UserProfilePermissions permissions = new UserProfilePermissions(member); if (!permissions.getCanEditMemberProfile(memberId)) { return ErrorText.ACCESS_DENIED.flashAndReturnView(request); } model.addAttribute("permissions", permissions); model.addAttribute("_activePageLink", "community"); try { UserProfileWrapper currentUserProfile = new UserProfileWrapper(memberId, request); populateUserWrapper(currentUserProfile, model); model.addAttribute("newsletterBean", new NewsletterBean(currentUserProfile.getUserId())); model.addAttribute("newsletterActive", ConfigurationAttributeKey.IS_MY_EMMA_ACTIVE.get()); model.addAttribute("memberCategories", MembersClient.listMemberCategories()); model.addAttribute("countrySelectItems", CountryUtil.getSelectOptions()); return EDIT_PROFILE_VIEW; } catch (MemberNotFoundException e) { return ErrorText.NOT_FOUND.flashAndReturnView(request); } } @RequestMapping(params = "updateError=true") public String updateProfileError(HttpServletRequest request, Model model, @RequestParam(required = false) boolean emailError, @RequestParam(required = false) boolean passwordError, @RequestParam Long memberId, Member loggedInMember) { UserProfilePermissions permissions = new UserProfilePermissions(loggedInMember); model.addAttribute("permissions", permissions); model.addAttribute("updateError", true); if (emailError) { model.addAttribute("emailError", true); } if (passwordError) { model.addAttribute("passwordError", true); } try { UserProfileWrapper currentUserProfile = new UserProfileWrapper(memberId, request); if (permissions.getCanEditMemberProfile(memberId)) { model.addAttribute("newsletterBean", new NewsletterBean(currentUserProfile.getUserBean().getUserId())); model.addAttribute("countrySelectItems", CountryUtil.getSelectOptions()); return EDIT_PROFILE_VIEW; } } catch (MemberNotFoundException e) { _log.warn("Could not create user profile for {}", memberId); return "showProfileNotInitialized"; } return SHOW_PROFILE_VIEW; } @PostMapping("{memberId}/edit") public String updateUserProfile(HttpServletRequest request, HttpServletResponse response, Model model, @PathVariable long memberId, @ModelAttribute UserBean updatedUserBean, BindingResult result, Member loggedInMember) throws IOException, MemberNotFoundException { UserProfilePermissions permissions = new UserProfilePermissions(loggedInMember); model.addAttribute("permissions", permissions); model.addAttribute("_activePageLink", "community"); model.addAttribute("countrySelectItems", CountryUtil.getSelectOptions()); if (!permissions.getCanEditMemberProfile(updatedUserBean.getUserId()) || memberId != updatedUserBean.getUserId()) { return ErrorText.NOT_FOUND.flashAndReturnView(request); } UserProfileWrapper currentUserProfile = new UserProfileWrapper(updatedUserBean.getUserId(), request); model.addAttribute("currentUserProfile", currentUserProfile); model.addAttribute("messageBean", new MessageBean()); model.addAttribute("userBean", updatedUserBean); boolean changedUserPart = false; boolean validationError = false; if (StringUtils.isNotBlank(updatedUserBean.getPassword())) { final boolean isAdminEditingOtherProfile = permissions.getCanAdmin() && !currentUserProfile.isViewingOwnProfile(); final String currentPassword = updatedUserBean.getCurrentPassword(); if (MembersClient.validatePassword(currentPassword.trim(), currentUserProfile.getUser().getUserId()) || isAdminEditingOtherProfile) { validator.validate(updatedUserBean, result, UserBean.PasswordChanged.class); if (!result.hasErrors()) { final String newPassword = updatedUserBean.getPassword().trim(); MembersClient.updatePassword(memberId, newPassword); changedUserPart = true; } else { validationError = true; return EDIT_PROFILE_VIEW; } } else { result.addError( new ObjectError("currentPassword", "Password change failed: Current password is incorrect.")); validationError = true; return EDIT_PROFILE_VIEW; } } boolean eMailChanged = false; if (updatedUserBean.getEmail() != null && !updatedUserBean.getEmail().trim().isEmpty() && !updatedUserBean.getEmail().equals(currentUserProfile.getUserBean().getEmailStored())) { validator.validate(updatedUserBean, result, UserBean.EmailChanged.class); if (!result.hasErrors()) { currentUserProfile.getUser().setEmailAddress(updatedUserBean.getEmail()); changedUserPart = true; eMailChanged = true; } else { validationError = true; return EDIT_PROFILE_VIEW; } } if (updatedUserBean.getFirstName() != null && !updatedUserBean.getFirstName().equals(currentUserProfile.getUserBean().getFirstName())) { validator.validate(updatedUserBean, result); if (!result.hasErrors()) { currentUserProfile.getUser().setFirstName(HtmlUtil.cleanAll(updatedUserBean.getFirstName())); changedUserPart = true; } else { validationError = true; _log.warn("First name change failed for userId: {}", currentUserProfile.getUser().getId_()); } } if (updatedUserBean.getLastName() != null && !updatedUserBean.getLastName().equals(currentUserProfile.getUserBean().getLastName())) { validator.validate(updatedUserBean, result); if (!result.hasErrors()) { currentUserProfile.getUser().setLastName(HtmlUtil.cleanAll(updatedUserBean.getLastName())); changedUserPart = true; } else { validationError = true; _log.warn("Last name change failed for userId: {}", currentUserProfile.getUser().getId_()); } } if (updatedUserBean.getCountryName() != null) { validator.validate(updatedUserBean, result); if (!result.hasErrors() && !updatedUserBean.getCountryName().isEmpty()) { currentUserProfile.getUser().setCountry(HtmlUtil.cleanAll(updatedUserBean.getCountryName())); changedUserPart = true; } else { validationError = true; _log.warn("Country name change failed for userId: {}", currentUserProfile.getUser().getId_()); } } changedUserPart = changedUserPart | updateUserProfile(currentUserProfile, updatedUserBean); if (validationError) { updatedUserBean.setImageId(currentUserProfile.getUserBean().getImageId()); return EDIT_PROFILE_VIEW; } else if (changedUserPart) { updatedUserBean.setImageId(currentUserProfile.getUser().getPortraitFileEntryId()); MembersClient.updateMember(currentUserProfile.getUser()); if (eMailChanged) { updatedUserBean.setEmailStored(updatedUserBean.getEmail()); sendUpdatedEmail(currentUserProfile.getUser()); } AlertMessage.CHANGES_SAVED.flash(request); return EDIT_PROFILE_VIEW; } else { response.sendRedirect("/web/guest/member/-/member/userId/" + memberId); return EDIT_PROFILE_VIEW; } } private boolean updateUserProfile(UserProfileWrapper currentUserProfile, UserBean updatedUserBean) { Member member = currentUserProfile.getUser(); String existingBio = member.getShortBio(); if (existingBio == null) { existingBio = ""; } boolean changedMember = false; if (!existingBio.equals(updatedUserBean.getShortBio())) { member.setShortBio(HtmlUtil.cleanSome(updatedUserBean.getShortBio(), "")); changedMember = true; } String existingCountry = member.getCountry(); if (!StringUtils.isEmpty(existingCountry)) { if (CountryUtil.getCodeForCounty(existingCountry) != null) { existingCountry = CountryUtil.getCodeForCounty(existingCountry); } } if (updatedUserBean.getCountryCode() != null && !updatedUserBean.getCountryCode().equals(existingCountry) && !StringUtils.isEmpty(updatedUserBean.getCountryCode())) { member.setCountry(CountryUtil.getCountryForCode(updatedUserBean.getCountryCode())); changedMember = true; } // TODO // if(changedExpando || changedUserPart) { // fireGoogleEvent = !profileWasComplete && profileIsComplete(); //} if (updatedUserBean.getImageId() != currentUserProfile.getUserBean().getImageId()) { try { FileEntry fe = FilesClient.getFileEntry(updatedUserBean.getImageId()); if (fe != null) { currentUserProfile.getUser().setPortraitFileEntryId(fe.getFileEntryId()); changedMember = true; } } catch (FileEntryNotFoundException e) { throw new IllegalStateException("No file entry found for imageId " + updatedUserBean.getImageId() + " for member " + updatedUserBean.getUserId()); } } final MessagingUserPreferences messagingPreferences = MessagingClient .getMessagingPreferencesForMember(currentUserProfile.getUser().getId_()); boolean changedMessagingPreferences = false; if (updatedUserBean.getSendEmailOnMessage() != messagingPreferences.getEmailOnReceipt()) { messagingPreferences.setEmailOnReceipt(updatedUserBean.getSendEmailOnMessage()); changedMessagingPreferences = true; } if (updatedUserBean.getSendEmailOnActivity() != messagingPreferences.getEmailOnActivity()) { messagingPreferences.setEmailOnActivity(updatedUserBean.getSendEmailOnActivity()); changedMessagingPreferences = true; } if (updatedUserBean.getSendDailyEmailOnActivity() != messagingPreferences.getEmailActivityDailyDigest()) { messagingPreferences.setEmailActivityDailyDigest(updatedUserBean.getSendDailyEmailOnActivity()); changedMessagingPreferences = true; } if (changedMessagingPreferences) { MessagingClient.updateMessagingPreferences(messagingPreferences); } if (changedMember) { MembersClient.updateMember(currentUserProfile.getUser()); } return changedMember || changedMessagingPreferences; } private void sendUpdatedEmail(Member user) { String messageSubject = TemplateReplacementUtil .replacePlatformConstants("Your email address on the <colab-name/> has been updated"); String messageBody = TemplateReplacementUtil.replacePlatformConstants("Dear " + user.getFirstName() + ",\n" + "\n" + "This is an automated message to confirm that you recently updated your email address on the <colab-name/> website.\n" + "\n" + "Your username: " + user.getScreenName() + "\n" + "Your updated email address: " + user.getEmailAddress() + "\n" + "\n" + "You can login with your username at <colab-url/>. If you have any questions or need additional help, simply reply to this message.\n" + "\n" + "Thank you for engaging on the <colab-name/>!\n"); InternetAddress addressFrom = TemplateReplacementUtil.getAdminFromEmailAddress(); EmailClient.sendEmail(addressFrom.getAddress(), user.getEmailAddress(), messageSubject, messageBody, false, addressFrom.getAddress(),user.getId_()); } @PostMapping("{memberId}/delete") public void deleteUserProfile(HttpServletRequest request, HttpServletResponse response, Model model, @PathVariable long memberId, Member loggedInMember) throws IOException { UserProfilePermissions permission = new UserProfilePermissions(loggedInMember); if (permission.getCanEditMemberProfile(memberId)) { MembersClient.deleteMember(memberId); } if (memberId == loggedInMember.getId_()) { response.sendRedirect("/logout"); } else { response.sendRedirect("/members"); } } }